﻿using System;
using System.Collections.Generic;
using System.Reflection;
using Gp.Scripts.Core;
using QFramework;
using UnityEngine;

namespace Gp.Scripts.Core {
    public delegate Dictionary<object, ModifierData> InitEntity<in T>(T entity) where T : IActionRequest;

    public delegate IEnumerable<KeyValuePair<object, ModifierValue>> WeakRule<in T>(T entity) where T : IActionRequest;


    public interface IWeakRuleWrapper {
        public string Name { get; }
        
        public Type EntityType { get; }


        public bool Condition(object entity);
    }

    public class WeakRuleWrapper<T> : IWeakRuleWrapper where T : class, IActionRequest {
        public string Name { get; set; }
        private readonly WeakRule<T> _weakRule;
        private readonly Func<T, bool> _condition;
        public Type EntityType => typeof(T);

        public WeakRuleWrapper(string name, WeakRule<T> weakRule, Func<T, bool> condition) {
            Name = name;
            _weakRule = weakRule;
            
            _condition = condition ?? ((_) => true);
        }

        public bool Condition(object entity) {
            return Condition(entity as T);
        }

        public bool Condition(T entity) {
            return _condition(entity);
        }
        public IEnumerable<KeyValuePair<object, ModifierValue>> Execute(T entity) {
            return _weakRule(entity);
        }
    }

    public delegate IActionResult StrongRule<in T>(T entity, IReadOnlyDictionary<object, ModifierData> modifies)
        where T : IActionRequest;


    public interface IActionRequest { }

    public interface IRuleGroup<T> where T : class, IActionRequest {
        List<WeakRuleWrapper<T>> WeakRuleWrappers { get; }
        public InitEntity<T> InitFunc { get; }

        StrongRule<T> CurStrongRule { get; }

        IActionResult DoRules(T entity, IEnumerable<WeakRuleWrapper<T>> weakRules = null);

        IRuleGroup<T> RuleChain(WeakRuleWrapper<T> wr);
        IRuleGroup<T> Final(StrongRule<T> sr);
    }

    public abstract class IActionResult {
        private string _log;

        public IActionResult Log(string log) {
            _log += log + "\n";
            return this;
        }

        public string Log() {
            return ToString() + '\n' + _log;
        }
    }


    public interface IGameAction {
        Type EntityType { get; }
        void InitAction();
        

        public static string GetActionName(IGameAction gameAction) {
            var foo = gameAction.GetType().GetCustomAttribute<ActionRuleAttribute>();

            return foo != null ? foo.Name : "未命名";
        }
    }

    public interface IGameAction<TEntity> : IGameAction where TEntity : class, IActionRequest {
        IRuleGroup<TEntity> RuleGroup { get; }
        IRuleGroup<TEntity> BuildRules();
    }


    [Serializable]
    public struct ModifierData {
        private readonly float _orgValue;
        public float num;
        public float percentage;

        public float Total() {
            return _orgValue * (1 + percentage) + num;
        }

        public int TotalInt() {
            return Mathf.RoundToInt(Total());
        }
        

        public ModifierData(float orgValue) : this() {
            _orgValue = orgValue;
        }
        public ModifierData(float orgValue, ModifierValue value){
            _orgValue = orgValue;
            this.num = value.num;
            this.percentage = value.percentage;
        }
        public void Add(ModifierData o) {
            this.num += o.num;
            this.percentage += o.percentage;
        }

        public ModifierData Add(ModifierValue v) {
            this.num += v.num;
            this.percentage += v.percentage;
            return this;
        }

        public static readonly ModifierData Zero = new() {num = 0, percentage = 0};
    }

    [Serializable]
    public struct ModifierValue {
        public static readonly ModifierValue Zero = new (0, 0);
        public int num;
        public int percentage;

        public ModifierValue(int num) : this() {
            this.num = num;
        }

        public ModifierValue(int num, int percentage) {
            this.num = num;
            this.percentage = percentage;
        }


        /// <summary>
        /// 填入的值为百分数
        /// </summary>
        /// <param name="percentage"></param>
        /// <returns></returns>
        public static ModifierValue Percentage(int percentage) {
            return new ModifierValue(0, percentage);
        }
        
        public static ModifierValue operator +(ModifierValue v1, ModifierValue v2) {
            return new ModifierValue(v1.num + v2.num, v1.percentage + v2.percentage);
        }

        public static ModifierValue operator -(ModifierValue v1, ModifierValue v2) {
            return new ModifierValue(v1.num - v2.num, v1.percentage - v2.percentage);
        }


        public float Result(float org) {
            return org * (1 + percentage) + num;
        }


        public int ResultInt(float org) {
            return Mathf.RoundToInt(Result(org));
        }
    }


    public class GeneralRuleGroup<T> : IRuleGroup<T> where T : class, IActionRequest {
        public InitEntity<T> InitFunc { get; private set; }
        public List<WeakRuleWrapper<T>> WeakRuleWrappers { get; } = new();
        public StrongRule<T> CurStrongRule { get; private set; }

        private GeneralRuleGroup() { }

        public static IRuleGroup<T> BindData(InitEntity<T> initFunc) {
            return new GeneralRuleGroup<T>() {InitFunc = initFunc};
        }

        public IRuleGroup<T> RuleChain(WeakRuleWrapper<T> wr) {
            WeakRuleWrappers.Add(wr);
            return this;
        }

        public IRuleGroup<T> Final(StrongRule<T> sr) {
            if (CurStrongRule != null)
                throw new Exception("重复设置强规则");
            CurStrongRule = sr;
            return this;
        }

        public IActionResult DoRules(T entity, IEnumerable<WeakRuleWrapper<T>> addRule = null) {
            var modifiers = InitFunc(entity);
            string wkLog = "";
            WeakRuleWrappers.ForEach(r => { WeakRuleProcess(r, entity, modifiers, ref wkLog); });
            addRule?.ForEach(r => { WeakRuleProcess(r, entity, modifiers, ref wkLog); });

            var result = "";
            if (CurStrongRule!= null) {
                foreach (var (key, value) in modifiers) {
                    result += $"[{GetName(key)}] = {value.Total()}\n";
                }
            }
            
            
            return CurStrongRule?.Invoke(entity, modifiers).Log("规则计算参数结果：" + result + "\n\n" + wkLog + "\n");

            string GetName(object o) {
                return o switch {
                    // StatField statField => statField.StatName(),
                    // SkillField skillField => skillField.FieldName(),
                    _ => o.ToString()
                };
            }
        }


        private static void WeakRuleProcess(WeakRuleWrapper<T> r, T entity,
            IDictionary<object, ModifierData> modifiers, ref string log) {

            if (!r.Condition(entity)) return;
            
            var dic = r.Execute(entity);
            log += "中间规则：" + r.Name + "\n";
            foreach (var (key, value) in dic) {
                if (modifiers.TryGetValue(key, out var data)) {
                    modifiers[key] = data.Add(value);
                }
                else {
                    throw new InvalidOperationException("无效属性：" + key.GetType());
                }
            }
        }
    }

    public abstract class ActionRequest : IActionRequest { }

    public abstract class AbstractGameAction<TEntity> : IGameAction<TEntity> where TEntity : class, IActionRequest {
        public IRuleGroup<TEntity> RuleGroup { get; private set; }

        public void InitAction() {
            RuleGroup ??= BuildRules();
        }

        /// <summary>
        /// 创建规则函数
        /// </summary>
        public abstract IRuleGroup<TEntity> BuildRules();

        public Type EntityType => typeof(TEntity);
        
        
        protected static T GetModel<T> () where T : class, IModel {
            return MainArchitecture.Interface.GetModel<T>();
        }
    }
}